package cn.clj1;

import cn.clj1.builtInFunctions.*;
import cn.clj1.cell.CljCell;
import cn.clj1.cell.CljCellType;
import cn.clj1.func.MyCljFunc;
import cn.clj1.recur.CljRecur;

import java.util.Hashtable;
import java.util.List;

/**
 * MyClojure 解释器
 * */
public class MyClojureInterp {
    // 全局 function 或者 宏展开
    public static Hashtable<String, MyCljFunc> funcs = new Hashtable<String, MyCljFunc>();
    // 全局变量
    public static Hashtable<String, Object> params = new Hashtable<String, Object>();

    /**
     * 执行 () 里面的内容
     * */
    public static CljCell Clj_list(CljCell cell, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        List<CljCell> lst = (List<CljCell>) cell.getCell();
        if (!lst.isEmpty()) {
            CljCell c = lst.get(0);
            switch (c.getCljCellType()) {
                case defn:
                    CljFunctions.Clj_list_defn(lst, funcHs, cljRecur, rs);
                    break;
                case symbol:
                    rs = CljMyFunc.Clj_list_symbol(lst, funcHs, cljRecur, rs);
                    break;
                case addition:
                    rs = CljMath.Clj_list_addition(lst, funcHs, cljRecur, rs);
                    break;
                case subtraction:
                    rs = CljMath.Clj_list_subtraction(lst, funcHs, cljRecur, rs);
                    break;
                case multiplication:
                    rs = CljMath.Clj_list_multiplication(lst, funcHs, cljRecur, rs);
                    break;
                case division:
                    rs = CljMath.Clj_list_division(lst, funcHs, cljRecur, rs);
                    break;
                case apply:
                    rs = CljMyFunc.Clj_list_apply(lst, funcHs, cljRecur, rs);
                    break;
                case cond:
                    rs = Cljlogic.Clj_list_cond(lst, funcHs, cljRecur, rs);
                    break;
                case if_clj:
                    rs = Cljlogic.Clj_list_if(lst, funcHs, cljRecur, rs);
                    break;
                case do_clj:
                    rs = CljLg.Clj_list_do(lst, funcHs, cljRecur, rs);
                    break;
                case and:
                    rs = Cljlogic.Clj_list_and(lst, funcHs, cljRecur, rs);
                    break;
                case or:
                    rs = Cljlogic.Clj_list_or(lst, funcHs, cljRecur, rs);
                    break;
                case equals_clj:
                    rs = Cljlogic.Clj_list_equals(lst, funcHs, cljRecur, rs);
                    break;
                case greater_than_clj:
                    rs = Cljlogic.Clj_list_greater_than(lst, funcHs, cljRecur, rs);
                    break;
                case less_than_clj:
                    rs = Cljlogic.Clj_list_less_than(lst, funcHs, cljRecur, rs);
                    break;
                case greater_equals_clj:
                    rs = Cljlogic.Clj_list_greater_equals(lst, funcHs, cljRecur, rs);
                    break;
                case less_equals_clj:
                    rs = Cljlogic.Clj_list_less_equals(lst, funcHs, cljRecur, rs);
                    break;
                case loop:
                    rs = CljLg.Clj_list_loop(lst, funcHs, cljRecur, rs);
                    break;
                case recur:
                    rs = CljLg.Clj_list_recur(lst, funcHs, cljRecur, rs);
                    break;
                case let:
                    rs = CljLg.Clj_list_let(lst, funcHs, cljRecur, rs);
                    break;
                case not:
                    rs = Cljlogic.Clj_list_not(lst, funcHs, cljRecur, rs);
                    break;
                case is_nil:
                    rs = Cljlogic.Clj_list_is_nil(lst, funcHs, cljRecur, rs);
                    break;
                case println:
                    CljCell cljCell = lst.get(1);
                    if (cljCell.getCljCellType() == CljCellType.symbol || cljCell.getCljCellType() == CljCellType.percent) {
                        CljCell cljCell1 = funcHs.get(cljCell.getCell().toString());
                        System.out.println(cljCell1.getCell().toString());
                    } else {
                        System.out.println(lst.get(1).toString());
                    }
                    break;
                    // 数据结构
                case set:
                    break;
                case hash_table:
                    rs = CljDataType.Clj_list_hash_table(lst, funcHs, cljRecur, rs);
                    break;
                case def:
                    rs = CljLg.Clj_list_def(lst, funcHs, cljRecur, rs);
                    break;
                case anonymous:
                    rs = CljMyFunc.Clj_list_fn_anonymous(lst, funcHs, cljRecur, rs);
                    break;
                case Clj_list:
                    // 匿名函数
                    if (((List<CljCell>)c.getCell()).get(0).getCljCellType() == CljCellType.fn) {
                        rs = CljMyFunc.Clj_list_fn(lst, funcHs, cljRecur, rs);
                    } else {
                        for (int i = 0; i < lst.size(); i++) {
                            CljCell cljCell1 = lst.get(i);
                            if (i == lst.size()) {
                                rs = eval(cljCell1, funcHs, cljRecur, rs);
                            } else {
                                eval(cljCell1, funcHs, cljRecur, rs);
                            }
                        }
                    }
                    break;
            }
        }

        return rs;
    }

    /**
     * 执行
     * */
    public static CljCell eval(CljCell cell, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        switch (cell.getCljCellType()) {
            case Clj_list:
                rs = Clj_list(cell, funcHs, cljRecur, rs);
                break;
            case list:
                rs = CljLg.my_list(cell, funcHs, cljRecur, rs);
                break;
            case braces:
                List<CljCell> lst = (List<CljCell>) cell.getCell();
                if (!lst.isEmpty() && lst.get(0).getCljCellType() == CljCellType.key_symbol) {
                    rs = CljDataType.Clj_list_hash_table(lst, funcHs, cljRecur, rs);
                }
                break;
            case hash_table:
                break;
            case set:
                break;
            case symbol:
                if (funcHs != null && funcHs.containsKey(cell.getCell().toString()))
                {
                    CljCell cljCell = funcHs.get(cell.getCell().toString());
                    rs.setCell(cljCell.getCell());
                    rs.setCljCellType(cljCell.getCljCellType());
                } else {
                    //throw new Exception(cell.getCell().toString() + "不存在！");
                    rs.setCljCellType(CljCellType.nil);
                }
                break;
            case string_clj:
                //rs = cell;
                rs.setCell(cell.getCell());
                rs.setCljCellType(CljCellType.string_clj);
                break;
            case number_clj:
                //rs = cell;
                rs.setCell(cell.getCell());
                rs.setCljCellType(CljCellType.number_clj);
                break;
            case boolean_clj:
                rs = cell;
                break;
            case date_clj:
                rs = cell;
                break;
            case long_clj:
                rs = cell;
                break;
            case char_clj:
                rs = cell;
                break;
            case short_clj:
                rs = cell;
                break;
            case byte_clj:
                rs = cell;
                break;
            case double_clj:
                rs = cell;
                break;
            case float_clj:
                rs = cell;
                break;
        }
        return rs;
    }
}
